package com.dodo.blog.servlet;

import com.dodo.blog.RequestCycle;
import com.dodo.blog.WebApplication;
import com.dodo.blog.model.Account;
import com.dodo.blog.model.Role;
import com.dodo.blog.ui.ajax.AjaxListenerHelper;
import com.dodo.blog.ui.ajax.IAjaxRequestHandler;
import com.dodo.blog.ui.authorization.Authorized;
import com.dodo.blog.ui.page.ErrorPage;
import com.dodo.blog.ui.page.Page;
import com.google.inject.Injector;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.logging.Logger;

/**
 * @author <a href="mailto:pohorelec@comvai.com">Jozef Pohorelec</a>
 */
public class ApplicationFilter
        implements Filter
{
    private Logger log = Logger.getLogger( ApplicationFilter.class.getName() );

    // TODO: make as singleton and inject by guice
    private static final WebApplication webApplication = WebApplication.get();

    private FilterConfig config;

    @Override
    public void init( FilterConfig filterConfig ) throws ServletException
    {
        this.config = filterConfig;
    }

    @Override
    @SuppressWarnings( value = "unchecked" )
    public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain )
            throws IOException, ServletException
    {
        HttpServletRequest httpRequest = ( HttpServletRequest ) request;
        HttpServletResponse httpResponse = ( HttpServletResponse ) response;

        // set request cycle
        RequestCycle.set( httpRequest, httpResponse, webApplication, config );

        // get path
        String path = httpRequest.getServletPath();
        Class pageClass = webApplication.getPageClassByPath( path );
        if ( pageClass == null )
        {
            chain.doFilter( request, response );
            return;
        }

        response.setContentType( "text/html" );
        response.setCharacterEncoding( "UTF-8" );

        try
        {
            // check authorization
            if ( !isAuthorized( pageClass ) )
            {
                Injector injector = RequestCycle.get().getInjector();
                Page page = injector.getInstance( WebApplication.get().getUnauthorizedPage() );
                response.getWriter().write( page.render() );

                return;
            }

            // handle ajax calls
            if ( AjaxListenerHelper.isAjax() )
            {
                String paramHandler = request.getParameter( IAjaxRequestHandler.PARAM_AJAX_HANDLER );
                if ( paramHandler != null )
                {
                    IAjaxRequestHandler handler = ( IAjaxRequestHandler ) Class.forName( paramHandler ).newInstance();
                    handler.handle();

                    return;
                }
            }

            // regular page calls
            Injector injector = RequestCycle.get().getInjector();
            Page page = ( Page ) injector.getInstance( pageClass );
            response.getWriter().write( page.render() );
        }
        catch ( Exception e )
        {
            processError( e, path, httpResponse );
        }
    }

    private void processError( Exception e, String path, HttpServletResponse httpResponse )
    {
        StringBuilder sb = new StringBuilder();
        sb.append( "Error occurred during creating page fot path[" ).append( path ).append( "]: " ).append( e ).append( ", cause: " ).append( e.getCause() ).append( "\n" );

        StringWriter sw = new StringWriter();
        e.printStackTrace( new PrintWriter( sw ) );
        log.severe( sw.toString() );

        try
        {
            ErrorPage errorPage = RequestCycle.get().getWebApplication().getErrorPage().newInstance();
            errorPage.setError( e );

            httpResponse.getWriter().write( errorPage.render() );
            httpResponse.setStatus( HttpServletResponse.SC_INTERNAL_SERVER_ERROR );
        }
        catch ( Exception e1 )
        {
            log.severe( "Error occurred during creating error page:" + e1 );
        }
    }

    private boolean isAuthorized( Class clazz )
    {
        Authorized authorizedRoles = ( Authorized ) clazz.getAnnotation( Authorized.class );
        boolean isAuthorized = false;
        if ( authorizedRoles != null )
        {
            Account account = WebApplication.get().getLoggedInAccount();
            if ( account != null )
            {
                for ( Role role : authorizedRoles.roles() )
                {
                    if ( account.getRole() == role )
                    {
                        isAuthorized = true;
                        break;
                    }
                }
            }
        }
        else
        {
            isAuthorized = true;
        }

        return isAuthorized;
    }

    @Override
    public void destroy()
    {
    }
}